
#include <BFont>
#include <BFile>
#include "IFontFamily.h"
#include "member_BFont.h"

using namespace BWE;

#ifdef linux
#include <unistd.h>
#include <locale.h>
#include <iconv.h>
#include <sys/time.h>

int WccToMbc(const BCode &code)
{
	const wchar_t *ustr = (const wchar_t *)code.str();
	char cstr[5];
	const char *loca = setlocale(LC_CTYPE, 0);
	size_t ret = wcstombs(cstr, ustr, 4 + 1);
	return *(int *)cstr;
}
int MbcToWcc(const BCode &code)
{
	const char *ustr = code.str();
	char cstr[3];
	cstr[0] = ustr[0];
	cstr[1] = ustr[1];
	cstr[2] = 0;
	const char *loca = setlocale(LC_CTYPE, 0);
	wchar_t wcs[100] = {0};
	size_t ret = mbstowcs(wcs, cstr, MB_CUR_MAX);
	return wcs[0];
}
#endif

#ifdef _WIN32
#include <Windows.h>
#undef min
#undef max
#undef near
#undef far

int WccToMbc(const BCode &code)
{
	const char *ustr = code.str();
	char cstr[5];
	int len = MultiByteToWideChar(CP_UTF8, 0, ustr, -1, 0, 0);
	int ret = MultiByteToWideChar(CP_UTF8, 0, ustr, 4, (LPWSTR)cstr, len);
	return *(int *)cstr;
}
int MbcToWcc(const BCode &code)
{
	const char *ustr = code.str();
	char cstr[3];
	cstr[0] = ustr[0];
	cstr[1] = ustr[1];
	cstr[2] = 0;
	wchar_t wstr[2];
	int len = MultiByteToWideChar(CP_ACP, 0, cstr, -1, NULL, 0);
	int ret = MultiByteToWideChar(CP_ACP, 0, cstr, -1, wstr, len);
	return wstr[0];
}

#endif

BMap<BString, FontFamilyHolder> &FontFamilyMap()
{
	static BMap<BString, FontFamilyHolder> familyMap;
	return familyMap;
}

member_BFont::member_BFont(BFont *font)
{
	boss = font;
	bold = false;
	italic = false;
	monospace = false;
	tabSize = 2;
	encoding = Encoding_None;
}
member_BFont::~member_BFont()
{
}

bool member_BFont::loadFamily()
{
	BString familyName = family;
	familyName.lower();
	if (bold)
		familyName << 'b';
	if (italic)
		familyName << 'i';

	auto &familyMap = FontFamilyMap();
	IFontFamily *font_family = familyMap(familyName);
	if (font_family)
	{
		fontFamily = font_family;
		encoding = fontFamily->encodingList.first();
		return true;
	}

	BString fileName = IFontFamily::SearchFileName(familyName);
	if (fileName.empty())
		return false;

	fontFamily = new IFontFamily(fileName);
	familyMap[familyName] = fontFamily;
	encoding = fontFamily->encodingList.first();
	return true;
}

inline FT_ULong ConvertCode(Encoding encoding, const BCode &code)
{
	FT_ULong ftcode = 0;
	if (code.num() == 1)
	{
		ftcode = code;
	}
	else if (code.num() == 2)
	{
		if (encoding == Encoding_Unicode)
			ftcode = MbcToWcc(code);
		else
		{
			const char *str = code.str();
			char *ptr = (char *)&ftcode;
			ptr[0] = str[1];
			ptr[1] = str[0];
			ptr[2] = 0;
			ptr[3] = 0;
		}
	}
	else if (code.num() == 3)
	{
		if (encoding == Encoding_GB2312)
			ftcode = WccToMbc(code);
		else
		{
			const unsigned char *str = (const unsigned char *)code.str();
			int va = str[2];
			int vb = str[1];
			int vc = str[0];
			va = va & 0x3f;
			vb = vb & 0x3f;
			vc = vc & 0x1f;
			vb = vb << 6;
			vc = vc << 12;
			ftcode = va + vb + vc;
		}
	}
	else if (code.num() == 4)
	{
		if (encoding == Encoding_GB2312)
			ftcode = WccToMbc(code);
		else
		{
			const unsigned char *str = (const unsigned char *)code.str();
			int va = str[3];
			int vb = str[2];
			int vc = str[1];
			int vd = str[0];
			va = va & 0x3f;
			vb = vb & 0x3f;
			vc = vc & 0x3f;
			vd = vc & 0x1f;
			vb = vb << 6;
			vc = vc << 12;
			vd = vd << 18;
			ftcode = va + vb + vc + vd;
		}
	}
	return ftcode;
}

CodeInfo *member_BFont::getCodeInfo(const BCode &code)
{
	if (!fontFamily)
		return 0;

	if (troubleCodes.contain(code))
		return 0;

	auto it = codeInfoMap.find(code);
	if (it.valid())
		return &(*it);

	FT_ULong ftcode = ConvertCode(encoding, code);

	if (ftcode)
	{
		CodeInfo &info = codeInfoMap[code];
		if (fontFamily->image(ftcode, size, info))
		{
			if (monospace)
				info.image->setFilter(BTexture::Filter_Linear);
			else
				info.image->setFilter(BTexture::Filter_Nearest);
			return &info;
		}
	}

	troubleCodes.append(code);
	return 0;
}
BSize member_BFont::codeSize(const BCode &code)
{
	if (code.num() > 1)
		return BSize(size, size);

	if (code == '\n')
		return BSize(size, size);

	if (code == ' ')
		return BSize(size / 2, size);

	if (code == '\t')
		return BSize(tabSize * size, size);

	if (monospace)
		return BSize(size / 2, size);

	CodeInfo *info = getCodeInfo(code);
	if (!info)
		return BSize(size, size);

	const BImage *image = info->image;
	if (!image)
		return BSize(size, size);

	int width = info->left + image->width();

	return BSize(width, size);
}

CodeInfo *member_BFont::getCodeInfo(const BCode &code, int size)
{
	if (this->size == size)
		return getCodeInfo(code);

	if (!fontFamily)
		return 0;

	if (troubleCodes.contain(code))
		return 0;

	CodeInfoMap &codeInfoMap = dynamicCodeInfoMap[size];
	auto it = codeInfoMap.find(code);
	if (it.valid())
		return &(*it);

	FT_ULong ftcode = ConvertCode(encoding, code);

	if (ftcode)
	{
		CodeInfo &info = codeInfoMap[code];
		if (fontFamily->image(ftcode, size, info))
			return &info;
	}

	troubleCodes.append(code);
	return 0;
}
BSize member_BFont::codeSize(const BCode &code, int size)
{
	if (code == '\n')
		return BSize(size, size);

	if (code == ' ')
		return BSize(size / 2, size);

	if (code == '\t')
		return BSize(tabSize * size, size);

	CodeInfo *info = getCodeInfo(code, size);
	if (!info)
		return BSize(size, size);

	const BImage *image = info->image;
	if (!image)
		return BSize(size, size);

	int width = info->left + image->width();

	return BSize(width, size);
}

CodeInfo *member_BFont::getLinearCodeInfo(const BCode &code)
{
	if (!fontFamily)
		return 0;

	if (troubleCodes.contain(code))
		return 0;

	auto it = linearCodeInfoMap.find(code);
	if (it.valid())
		return &(*it);

	FT_ULong ftcode = ConvertCode(encoding, code);

	if (ftcode)
	{
		CodeInfo &info = linearCodeInfoMap[code];
		if (fontFamily->image(ftcode, size, info))
		{
			info.image->setFilter(BTexture::Filter_Linear);
			return &info;
		}
	}

	troubleCodes.append(code);
	return 0;
}
BSizef member_BFont::linearCodeSize(const BCode &code)
{
	if (code == '\n')
		return BSize(size, size);

	if (code == ' ')
		return BSize(size / 2, size);

	if (code == '\t')
		return BSize(tabSize * size, size);

	CodeInfo *info = getLinearCodeInfo(code);
	if (!info)
		return BSizef((float)size);

	BImage *image = info->image;
	if (!image)
		return BSizef((float)size);
	image->setFilter(BTexture::Filter_Linear);
	int width = info->left + image->width();

	return BSizef((float)width, (float)size);
}
